/*
 *  chunkList.h
 *  kangmodb
 *
 *  Created by 강모 김 on 11. 5. 1..
 *  Copyright 2011 강모소프트. All rights reserved.
 *
 */

#ifndef _KD_CHUNK_LIST_H_
#define _KD_CHUNK_LIST_H_ (1)

#include "list.h"

/** @brief The header stored in the root chunk in the chunk list. Root chunk is the first chunk in the list.
 */
typedef struct stgRootChunkHeader
{
	/** @brief Keeps head/tail link of the double linked list.
	 */
	list_t chunkList;
} stgRootChunkHeader;

/** @brief The header stored in chunks in the chunk list.  
 */
typedef struct stgChunkHeader
{
	/** @brief Keeps prev/next link of the double linked list.
	 */
	list_node_t nodeLinks;
} stgChunkHeader;

/** @brief Maintain a double linked list that keeps track of multiple memory chunks.
 * 
 * - stgTransLogBuffer uses stgChunkList to keep multiple log chunks for a transaction.
 * - stgTable uses stgChunkList to keep multiple memory chunks to keep (key, data) pairs.
 */
class stgChunkList
{
private :
	/** @brief The first chunk in this chunk list. It has head/tail of the chunk list. 
	 */
    stgRootChunkHeader * rootChunk_;
	
	/** @brief The next chunk to iterate. Used by both iterate and reverseIterate.
	 */
	mutable void * nextIterationChunk_;
	
public :

	/** @brief The size of unusable memory at the beginning of added chunk. 
	 */
	static const int CHUNK_UNUSABLE_SIZE = MAX(sizeof(stgRootChunkHeader), sizeof(stgChunkHeader));
	
	stgChunkList()
	{
		rootChunk_ = NULL;
		nextIterationChunk_ = NULL;
	}
	~stgChunkList()
	{
	}
	/** @brief See if the chunk list is empty.
	 */
	bool empty() const 
	{
		// If the root chunk(the first chunk) is NULL, it means the list is empty.
		return rootChunk_ == NULL;
	}
	/** @brief Add a chunk to the chunk list. 
	 */
	KD_VOID addChunk( const void * chunkAddress )
	{
		KD_TRY
		{
			if (rootChunk_ == NULL ) // No root chunk. Set the chunk as the root chunk
			{
				rootChunk_ = (stgRootChunkHeader*) chunkAddress;
				(void) list_init( &rootChunk_->chunkList ); 
			}
			else
			{
				stgChunkHeader * chunkHeader = (stgChunkHeader*) chunkAddress;
				(void) list_add_tail( &rootChunk_->chunkList, &chunkHeader->nodeLinks); 
			}
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	
	/** @brief Remove a chunk from the list.
	 */
	KD_VOID removeChunk( const void * chunkAddress )
	{
		KD_ASSERT( rootChunk_ != NULL );

		KD_TRY
		{
			// If the root chunk is removed, all chunks in the list are removed as well. This is by design.
			if (chunkAddress == rootChunk_ )
			{
				rootChunk_ = NULL;
			}
			else // Not a root chunk, remove from the list 
			{
				stgChunkHeader * chunkHeader = (stgChunkHeader*) chunkAddress;
				
				(void) list_remove( &rootChunk_->chunkList, &chunkHeader->nodeLinks );
			}
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	
	/** @brief Initialize iterations.
	 */
	KD_VOID initIteration() const
	{
		KD_TRY
		{
			nextIterationChunk_ = rootChunk_;
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	} 

	/** @brief Iterate a chunk. If no more chunk, *chunkAddress is set to NULL.
	 */
	KD_VOID iterate( void ** chunkAddress ) const
	{
		KD_DASSERT( chunkAddress != NULL );
		
		KD_TRY
		{
			if (nextIterationChunk_ == NULL) // End of iteration
			{
				*chunkAddress = NULL;
			}
			else 
			{
				*chunkAddress = nextIterationChunk_ ;
				
				if ( nextIterationChunk_ == rootChunk_ )
				{
					// Root chunk was the first one to iterate chunks. Next chunk to iterate is the one in head in the list.
					nextIterationChunk_ = rootChunk_->chunkList.head;
				}
				else 
				{
					nextIterationChunk_ = ((stgChunkHeader*) nextIterationChunk_)->nodeLinks.next;
				}

				if ( nextIterationChunk_ == LIST_NODE_NULL( & rootChunk_->chunkList ) ) // Reached at the end of iteration?
				{
					nextIterationChunk_ = NULL;
				}
			}
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	} 

	/** @brief Initialize iterations in reverse order.
	 */
	KD_VOID initReverseIteration() const
	{
		KD_TRY
		{
			if ( rootChunk_ == NULL ) // No chunk is added yet.
			{
				nextIterationChunk_ = NULL; // Nothing to iterate.
			}
			else 
			{
				// Start the reverse iteration from the tail of the list.
				nextIterationChunk_ = rootChunk_->chunkList.tail;
			}
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	} 

	/** @brief Iterate a chunk in reverse order. If no more chunk, *chunkAddress is set to NULL.
	 */
	KD_VOID reverseIterate( void ** chunkAddress ) const
	{
		KD_DASSERT( chunkAddress != NULL );
		
		KD_TRY
		{
			if (nextIterationChunk_ == NULL) // End of iteration
			{
				*chunkAddress = NULL;
			}
			else 
			{
				*chunkAddress = nextIterationChunk_ ;
				
				if ( nextIterationChunk_ == rootChunk_ )
				{
					// Root chunk was the last one to iterate chunks in reverse order.
					nextIterationChunk_ = NULL;
				}
				else 
				{
					nextIterationChunk_ = ((stgChunkHeader*) nextIterationChunk_)->nodeLinks.prev;
				}
				
				if ( nextIterationChunk_ == LIST_NODE_NULL( & rootChunk_->chunkList ) ) // Reached at the end of the list?
				{
					// We need to iterate the root chunk, which is the last one to iterate in reverse order.
					nextIterationChunk_ = rootChunk_;
				}
			}
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	} 
};

#endif /* _KD_CHUNK_LIST_H_ */